package crmdna.common;

import static crmdna.common.AssertUtils.ensureNotNull;

import java.util.Calendar;
import java.util.Date;
import java.util.Random;

import crmdna.common.APIResponse.Status;

public class DateUtils {

	public enum DateRange {
		LAST_24_HOURS, LAST_48_HOURS, LAST_72_HOURS, LAST_7_DAYS, LAST_15_DAYS, LAST_30_DAYS, LAST_90_DAYS, LAST_180_DAYS, LAST_365_DAYS
	}

	public static long getMilliSecondsFromDateRange(DateRange dateRange) {
		ensureNotNull(dateRange);

		final long NUM_MS_PER_DAY = 86400 * 1000;

		if (dateRange == DateRange.LAST_24_HOURS)
			return NUM_MS_PER_DAY;

		if (dateRange == DateRange.LAST_48_HOURS)
			return NUM_MS_PER_DAY * 2;

		if (dateRange == DateRange.LAST_72_HOURS)
			return NUM_MS_PER_DAY * 3;

		if (dateRange == DateRange.LAST_7_DAYS)
			return NUM_MS_PER_DAY * 7;

		if (dateRange == DateRange.LAST_15_DAYS)
			return NUM_MS_PER_DAY * 15;

		if (dateRange == DateRange.LAST_30_DAYS)
			return NUM_MS_PER_DAY * 30;

		if (dateRange == DateRange.LAST_90_DAYS)
			return NUM_MS_PER_DAY * 90;

		if (dateRange == DateRange.LAST_180_DAYS)
			return NUM_MS_PER_DAY * 180;

		if (dateRange == DateRange.LAST_365_DAYS)
			return NUM_MS_PER_DAY * 365;

		throw new APIException().status(Status.ERROR_RESOURCE_INCORRECT)
				.message(
						"Cannot get milli seconds from data range ["
								+ dateRange + "]");
	}

	public static int toYYYYMMDD(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);

		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH) + 1;
		int day = calendar.get(Calendar.DAY_OF_MONTH);

		int yyyymmdd = day + month * 100 + year * 10000;
		return yyyymmdd;
	}

	public static Date toDate(int yyyymmdd) {

		if (!isFormatInYYYYMMDD(yyyymmdd)) {
			Utils.throwIncorrectSpecException("[" + yyyymmdd
					+ "] is not in yyymmdd format");
		}

		String s = yyyymmdd + "";

		int year = Integer.parseInt(s.substring(0, 4));
		int month = Integer.parseInt(s.substring(4, 6));
		int day = Integer.parseInt(s.substring(6, 8));

		Calendar calendar = Calendar.getInstance();
		calendar.set(year, month - 1, day);

		return calendar.getTime();
	}

	public static boolean isFormatInYYYYMMDD(int date) {
		String s = "" + date;

		if (s.length() != 8)
			return false;

		int year = Integer.parseInt(s.substring(0, 4));
		int month = Integer.parseInt(s.substring(4, 6));
		int day = Integer.parseInt(s.substring(6, 8));

		if ((year < 1970) || (year > 2050))
			return false;

		if ((month < 1) || (month > 12))
			return false;

		if ((day < 1) || (day > 31))
			return false;

		if (month == 2) {
			if (day > 29)
				return false;

			if ((day == 29) && (year % 4 != 0))
				return false;
		}

		if ((month == 4) || (month == 6) || (month == 9) || (month == 11))
			if (day == 31)
				return false;

		return true;
	}

	public static String toDDMMM(int dateYYYYMMDD) {
		ensureFormatYYYYMMDD(dateYYYYMMDD);

		String s = "" + dateYYYYMMDD;

		int month = Integer.parseInt(s.substring(4, 6));
		int day = Integer.parseInt(s.substring(6, 8));

		return day + " " + get3CharMonth(month);
	}

	public static String get3CharMonth(int month) {
		if (1 == month)
			return "Jan";

		if (2 == month)
			return "Feb";

		if (3 == month)
			return "Mar";

		if (4 == month)
			return "Apr";

		if (5 == month)
			return "May";

		if (6 == month)
			return "Jun";

		if (7 == month)
			return "Jul";

		if (8 == month)
			return "Aug";

		if (9 == month)
			return "Sep";

		if (10 == month)
			return "Oct";

		if (11 == month)
			return "Nov";

		if (12 == month)
			return "Dec";

		throw new APIException(Status.ERROR_RESOURCE_INCORRECT,
				"Invalid month [" + month + "]");
	}

	public static String getDurationAsString(int startYYYYMMDD, int endYYYYMMDD) {
		ensureFormatYYYYMMDD(startYYYYMMDD);
		ensureFormatYYYYMMDD(endYYYYMMDD);

		if (startYYYYMMDD > endYYYYMMDD)
			throw new APIException(Status.ERROR_RESOURCE_INCORRECT,
					"Start date should not be greater than end date");

		if (startYYYYMMDD == endYYYYMMDD)
			return toDDMMMYY(startYYYYMMDD);

		String s = "" + startYYYYMMDD;
		int yearStart = Integer.parseInt(s.substring(0, 4));
		int monthStart = Integer.parseInt(s.substring(4, 6));
		int dayStart = Integer.parseInt(s.substring(6, 8));

		s = "" + endYYYYMMDD;
		int yearEnd = Integer.parseInt(s.substring(0, 4));
		int monthEnd = Integer.parseInt(s.substring(4, 6));

		if (yearStart == yearEnd) {
			if (monthStart == monthEnd) {
				return dayStart + " - " + toDDMMMYY(endYYYYMMDD);
			}

			return toDDMMM(startYYYYMMDD) + " - " + toDDMMMYY(endYYYYMMDD);
		}

		return toDDMMM(startYYYYMMDD) + " - " + toDDMMMYY(endYYYYMMDD);
	}

	public static String getDateDiff(long startTimeInSeconds,
			long endTimeInSeconds) {
		long diff = endTimeInSeconds - startTimeInSeconds;
		if (diff < 0)
			Utils.throwIncorrectSpecException("startTimeInSeconds greater than endTimeInSeconds");

		if (diff < 60)
			return diff + " seconds";

		if (diff < 3600) {
			long min = Math.round(diff / 60.0);
			return min + " minute(s)";
		}

		if (diff < 3600 * 24) {
			long hours = Math.round(diff / 3600.0);
			return hours + " hour(s)";
		}

		long days = Math.round(diff / 3600.0 / 24.0);
		return days + " day(s)";
	}

	public static String toDDMMMYY(int dateYYYYMMDD) {
		ensureFormatYYYYMMDD(dateYYYYMMDD);

		String s = "" + dateYYYYMMDD;

		int year = Integer.parseInt(s.substring(2, 4));
		int month = Integer.parseInt(s.substring(4, 6));
		int day = Integer.parseInt(s.substring(6, 8));

		return day + " " + get3CharMonth(month) + " " + year;
	}

	public static void ensureFormatYYYYMMDD(int date) {

		boolean valid = isFormatInYYYYMMDD(date);
		if (!valid)
			throw new APIException(Status.ERROR_RESOURCE_INCORRECT, "[" + date
					+ "] is not valid. Date should be valid in YYYYMMDD format");
	}

	public static long getNanoSeconds(Date timestamp) {
		// returns time in nano seconds
		
		// system clocks are only accurate up to millisecond precision 
		// get the time in ms and add a random no between 0 and 1 million to convert into nanoseconds

		return timestamp.getTime() * 1000000 + new Random().nextInt(999999);
	}
}
